package com.ctg.behavior.calc.impala.builder.funnel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.ctg.behavior.calc.common.CtStepEvent;
import com.ctg.behavior.calc.impala.helper.CtFunnelCalcHelper;
import com.ctg.behavior.calc.impala.report.funnel.CtCalcFunnelStepReport;
import com.ctg.behavior.calc.impala.utils.CtStepUtils;
import com.ctg.behavior.calc.utils.CtCalcDaysUtils;
import com.ctg.behavior.calc.utils.CtCalcPropertyUtils;
import com.ctg.behavior.common.consts.CtFieldConsts;
import com.ctg.behavior.common.funnel.CtFunnelCalcData;
import com.ctg.behavior.common.funnel.CtFunnelDetail;
import com.ctg.behavior.common.funnel.CtFunnelDetailStep;
import com.ctg.behavior.common.funnel.CtFunnelDetailStepRow;
import com.ctg.behavior.common.funnel.CtFunnelOverviewRow;
import com.ctg.behavior.common.items.EventItem;
import com.ctg.behavior.utils.CtDoubleUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class CtFunnelCalcResultBuilder {
	
	// byValueList, KEY: 数组值拼接成，形如, "" + "@" + ""
	private static ThreadLocal<LinkedHashMap<String, List<String>>> indexedByValues = new ThreadLocal<>();
	// KEY: step + "@" + event + "@" + day + "@" + byValues
	private static ThreadLocal<LinkedHashMap<String, Double>> indexedQueryResults = new ThreadLocal<>();
	// KEY: step + "@" + event + "@" + day + "@" + byValues
	private static ThreadLocal<Map<String, CtFunnelOverviewRow>> indexedOverviewRows = new ThreadLocal<>();
	// KEY: step + "@" + event + "@" + day + "@" + byValues
	private static ThreadLocal<Map<String, CtFunnelDetailStepRow>> indexedStepsRows = new ThreadLocal<>();

	public void resetResultBuilderThreadLocal() {
		{
			LinkedHashMap<String, List<String>> localV = new LinkedHashMap<>();
			indexedByValues.set(localV);
		}
		{
			LinkedHashMap<String, Double> localV = new LinkedHashMap<>();
			indexedQueryResults.set(localV);
		}
		{
			Map<String, CtFunnelOverviewRow> localV = new HashMap<>();
			indexedOverviewRows.set(localV);
		}
		{
			Map<String, CtFunnelDetailStepRow> localV = new HashMap<>();
			indexedStepsRows.set(localV);
		}
	}

	public CtFunnelCalcData doBuilder(CtFunnelCalcData calcData, List<LinkedHashMap<String, Object>> queryResults, CtCalcFunnelStepReport reportFunnelStep) {

		// 1.初始化(填充) - dateList & byFields
		calcData = this.doDateListAndByFieldsBuilder(calcData, reportFunnelStep);
		log.info("doBuilder-dateListAndByFields, calcResult={}", JSON.toJSONString(calcData));
		// 2.初始化(填充) - byValues & byValueList
		calcData = this.doByValuesBuilder(calcData, queryResults, reportFunnelStep);
		log.info("doBuilder-doByValuesBuilder, calcResult={}", JSON.toJSONString(calcData));
		// 3.对查询结果进行组建，作为计算结果的中间数据
		for (LinkedHashMap<String, Object> oneQueryRecord : queryResults) {
			String calcResultKey = CtFunnelCalcHelper.buildQueryResultKey(oneQueryRecord, reportFunnelStep.getStepNo());
			//Double resultValue = Double.valueOf(oneQueryRecord.get(CtFieldConsts.FIELD_KEY_TOTAL).toString());
			Double resultValue = 0.0D;
			Object retObj = oneQueryRecord.get(CtFieldConsts.FIELD_KEY_TOTAL);
			if(retObj != null) {
				resultValue = Double.valueOf(retObj.toString());
			}
			indexedQueryResults.get().put(calcResultKey, resultValue);
		}
		// 4.events - 准备步骤事件基础数据(必须有序-按步骤顺序)
		if (calcData.getEvents().isEmpty()) {
			Integer eventIndex = new Integer(0);
			for (String eventId : reportFunnelStep.getEventIds()) {
				EventItem oneItem = new EventItem();
				oneItem.setEventId(eventId);
				oneItem.setCname(reportFunnelStep.getEventCNames().get(eventIndex));
				calcData.getEvents().add(oneItem);
				eventIndex++;
			}
		}

		// 5.funnelDetail - 初始化(不能覆盖上次的数据)
		calcData = this.doInitOrAppendFunnelDetailBuilder(calcData);
		log.info("doBuilder-initOrAppend, calcResult={}", JSON.toJSONString(calcData));

		// 6. 计算funnelDetail
		String theEvent = reportFunnelStep.getEventId();
		for (int funnelDetailIndex = 1; funnelDetailIndex < calcData.getDateList().size(); funnelDetailIndex++) {
			String theDay = calcData.getDateList().get(funnelDetailIndex);
			log.info("theDay={}", theDay);
			// List<String> sumByValues = calcResult.getByValuesList().get(0);
			for (int byValuesIndex = 0; byValuesIndex < calcData.getByValuesList().size(); byValuesIndex++) {
				CtStepEvent theStepEvent = CtStepUtils.buildStepEvent(theEvent, reportFunnelStep.getStepNo());
				// doBuilder-funnelDetail-overview-oneRow
				calcData = this.doFunnelDetailOverviewRowBuilder(byValuesIndex, calcData, theStepEvent, theDay);
				// doBuilder-funnelDetail-steps-oneRow
				calcData = this.doFunnelDetailStepsRowBuilder(byValuesIndex, calcData, theStepEvent, theDay);
			}
		}
		log.info("doBuilder-funnelDetail, calcResult={}", JSON.toJSONString(calcData));

		// 7. 计算 CtFunnelDetailStep
		for (CtFunnelDetail funnelDetail : calcData.getFunnelDetail()) {
			for (int stepIndex = 0; stepIndex < funnelDetail.getSteps().size(); stepIndex++) {
				CtFunnelDetailStep detailStep = funnelDetail.getSteps().get(stepIndex);
				CtFunnelDetailStepRow stepRow = detailStep.getRows().get(0);
				detailStep.setConvertedUser(stepRow.getConvertedUser());
				if (stepIndex <= 0) {
					continue;
				}
				CtFunnelDetailStep uplayerDetailStep = funnelDetail.getSteps().get(stepIndex - 1);
				Double theRate = 0.0D;
				if (uplayerDetailStep.getConvertedUser() > 0 && detailStep.getConvertedUser() > 0) {
					theRate = 100D * detailStep.getConvertedUser() / uplayerDetailStep.getConvertedUser();
					theRate = CtDoubleUtils.format2Scale(theRate);
					detailStep.setConversionRate(theRate);
				}
			}
			List<List<CtFunnelOverviewRow>> detailOverview = funnelDetail.getOverview();
			if (detailOverview.isEmpty()) {
				continue;
			}
			List<CtFunnelOverviewRow> overviewRows = detailOverview.get(0);
			if (overviewRows.isEmpty()) {
				log.error("the sum overviewRows is empty, funnelDetail={}", JSON.toJSONString(funnelDetail));
				continue;
			}
			CtFunnelOverviewRow row = overviewRows.get(overviewRows.size() - 1);
			funnelDetail.setCompletionRate(row.getConversionRate());
		}
		log.info("doBuilder-funnelDetailStepObject, calcResult={}", JSON.toJSONString(calcData));

		return calcData;
	}

	public CtFunnelCalcData doInitOrAppendFunnelDetailBuilder(CtFunnelCalcData calcData) {

		if (calcData.getFunnelDetail().isEmpty()) {
			// 初始化(首次): funnelDetail数组(大小与dateList相同)
			for (int index = 0; index < calcData.getDateList().size(); index++) {
				CtFunnelDetail funnelDetail = new CtFunnelDetail();
				if (index == 0) {
					funnelDetail.setCompletionRate(100.0D);
				}
				calcData.getFunnelDetail().add(funnelDetail);
				// 初始化(首次): funnelDetail-steps:(大小与events相同)
				for (int eventIndex = 0; eventIndex < calcData.getEvents().size(); eventIndex++) {
					CtFunnelDetailStep step = new CtFunnelDetailStep();
					if (0 == eventIndex) {
						step.setConversionRate(100.0D);
					} else {
						step.setConversionRate(0.0D);
					}
					step.setConvertedUser(0.0D);
					step.setEventId(calcData.getEvents().get(eventIndex).getEventId());
					funnelDetail.getSteps().add(step);
				}
			}
		}

		int lastByValuesListSize = calcData.getFunnelDetail().get(0).getOverview().size();
		List<String> indexedByValuesKeys = new ArrayList<String>(indexedByValues.get().keySet());
		for (int byValueIndex = lastByValuesListSize; byValueIndex < indexedByValuesKeys.size(); byValueIndex++) {
			int funnelDetailIndex = 0;
			for (CtFunnelDetail funnelDetail : calcData.getFunnelDetail()) {
				// 初始化或追加-Overview中的ByValues记录(按分组新增的记录)
				String theDay = calcData.getDateList().get(funnelDetailIndex);
				List<List<CtFunnelOverviewRow>> overviewRows = funnelDetail.getOverview();
				List<CtFunnelOverviewRow> oneEventRows = new ArrayList<>();
				overviewRows.add(oneEventRows);
				for (int eventIndex = 0; eventIndex < calcData.getEvents().size(); eventIndex++) {
					CtFunnelOverviewRow oneRow = new CtFunnelOverviewRow();
					if (0 == eventIndex) {
						oneRow.setCompletionRate(100.0D);
						oneRow.setConversionRate(100.0D);
					} else {
						oneRow.setCompletionRate(0.0D);
						oneRow.setConversionRate(0.0D);
					}
					oneRow.setConvertedUser(0.0D);
					oneEventRows.add(oneRow);
					//
					String event = calcData.getEvents().get(eventIndex).getEventId();
					CtStepEvent stepEvent = CtStepUtils.buildStepEvent(event, eventIndex);
					String indexedByValuesKey = indexedByValuesKeys.get(byValueIndex);
					List<String> byValues = indexedByValues.get().get(indexedByValuesKey);
					String overviewRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(theDay, byValues, stepEvent);
					indexedOverviewRows.get().put(overviewRowKey, oneRow);
				}

				// 初始化或追加-Step中的ByValues记录(按分组初始化或追加记录)
				for (int eventIndex = 0; eventIndex < calcData.getEvents().size(); eventIndex++) {
					List<CtFunnelDetailStep> steps = funnelDetail.getSteps();
					CtFunnelDetailStep step = steps.get(eventIndex);
					List<CtFunnelDetailStepRow> stepRows = step.getRows();
					CtFunnelDetailStepRow stepRow = new CtFunnelDetailStepRow();
					if (0 == eventIndex) {
						stepRow.setConversionRate(100.0D);
					} else {
						stepRow.setConversionRate(0.0D);
					}
					stepRow.setConvertedUser(0.0D);
					stepRow.setMedianConvertedTime(0.0D);
					stepRow.setWastageUser(0.0D);
					stepRows.add(stepRow);
					//
					String event = calcData.getEvents().get(eventIndex).getEventId();
					CtStepEvent stepEvent = CtStepUtils.buildStepEvent(event, eventIndex);
					String indexedByValuesKey = indexedByValuesKeys.get(byValueIndex);
					List<String> byValues = indexedByValues.get().get(indexedByValuesKey);
					String overviewRowKey = CtFunnelCalcHelper.buildStepsRowsKey(theDay, byValues, stepEvent);
					indexedStepsRows.get().put(overviewRowKey, stepRow);
				}
				funnelDetailIndex++;
			}
		}

		return calcData;
	}

	public CtFunnelCalcData doFunnelDetailOverviewRowBuilder(Integer byValuesIndex, CtFunnelCalcData calcData, CtStepEvent theStepEvent, String theDay) {

		String sumDay = calcData.getDateList().get(0);
		List<String> sumByValues = calcData.getByValuesList().get(0);

		List<String> oneByValues = calcData.getByValuesList().get(byValuesIndex);
		log.info("theStepEvent={}", JSON.toJSONString(theStepEvent));
		CtStepEvent uplayerStepEvent = null;
		if (theStepEvent.getStepNo() > 0) {
			uplayerStepEvent = this.getUplayerStepEvent(theStepEvent.getStepNo(), calcData);
			log.info("uplayerStepEvent={}", JSON.toJSONString(uplayerStepEvent));
		}
		// 1. 获取对应的queryResult Value(total字段值)
		String calcResultKey = CtFunnelCalcHelper.buildQueryResultKey(theDay, oneByValues, theStepEvent);
		Double resultValue = indexedQueryResults.get().get(calcResultKey);
		if (resultValue == null) {
			// resultValue = Double.valueOf(0);
			return calcData;
		}
		// 2. 填充对应的Overview Row
		String overviewRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(theDay, oneByValues, theStepEvent);
		CtFunnelOverviewRow oneOverviewRow = indexedOverviewRows.get().get(overviewRowKey);
		oneOverviewRow.setConvertedUser(oneOverviewRow.getConvertedUser() + resultValue);
		if (uplayerStepEvent != null) {
			// 计算本ROW记录转化率
			Double theRate = 0.0D;
			String uplayerOverviewRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(theDay, oneByValues, uplayerStepEvent);
			CtFunnelOverviewRow uplayerOverviewRow = indexedOverviewRows.get().get(uplayerOverviewRowKey);
			Double uplayerValue = uplayerOverviewRow.getConvertedUser();
			if (resultValue > 0 && uplayerValue > 0) {
				theRate = 100.0D * resultValue / uplayerValue;
				theRate = CtDoubleUtils.format2Scale(theRate);
			}
			oneOverviewRow.setConversionRate(theRate);
			oneOverviewRow.setCompletionRate(theRate);
		}

		// 3. 累加到本OverviewRows的小计
		if (!CtFunnelCalcHelper.isSameByValues(sumByValues, oneByValues)) {
			// 本OverviewRows中的小计Row(第一行),即：本Rows中的byValuesIndex为0的Row
			String sumByValuesOverviewRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(theDay, sumByValues, theStepEvent);
			CtFunnelOverviewRow sumByValuesOverviewRow = indexedOverviewRows.get().get(sumByValuesOverviewRowKey);
			// 累加到对应的OverviewRow
			sumByValuesOverviewRow.setConvertedUser(sumByValuesOverviewRow.getConvertedUser() + resultValue);

			if (null != uplayerStepEvent) {
				// 计算本ROW记录转化率
				Double theRate = 0.0D;
				String uplayerSumByValuesOverviewRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(theDay, sumByValues, uplayerStepEvent);
				CtFunnelOverviewRow uplayerRow = indexedOverviewRows.get().get(uplayerSumByValuesOverviewRowKey);
				Double uplayerValue = uplayerRow.getConvertedUser();
				theRate = Double.valueOf(0);
				if (uplayerValue != null && uplayerValue > 0) {
					theRate = 100.0D * sumByValuesOverviewRow.getConvertedUser() / uplayerValue;
					theRate = CtDoubleUtils.format2Scale(theRate);
				}
				sumByValuesOverviewRow.setConversionRate(theRate);
				sumByValuesOverviewRow.setCompletionRate(theRate);
			}
		}

		// 4. 总体中的Rows, 即：funnelDetailIndex为[0]中的funnelDetail, byValues/event本次循化对应
		String sumDayOverviewRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(sumDay, oneByValues, theStepEvent);
		CtFunnelOverviewRow sumDayOverviewRow = indexedOverviewRows.get().get(sumDayOverviewRowKey);
		// 累加到对应的OverviewRow
		sumDayOverviewRow.setConvertedUser(sumDayOverviewRow.getConvertedUser() + resultValue);

		if (null != uplayerStepEvent) {
			// 计算本ROW记录转化率
			Double theRate = 0.0D;
			String uplayerSumDayOverviewRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(sumDay, oneByValues, uplayerStepEvent);
			CtFunnelOverviewRow uplayerRow = indexedOverviewRows.get().get(uplayerSumDayOverviewRowKey);
			Double uplayerValue = uplayerRow.getConvertedUser();
			if (uplayerValue > 0) {
				theRate = 100.0D * sumDayOverviewRow.getConvertedUser() / uplayerValue;
				theRate = CtDoubleUtils.format2Scale(theRate);
			}
			sumDayOverviewRow.setConversionRate(theRate);
			sumDayOverviewRow.setCompletionRate(theRate);
		}

		if (!CtFunnelCalcHelper.isSameByValues(sumByValues, oneByValues)) {
			// 5. 总体Rows中的总统计
			String sumDayAndSumByValuesRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(sumDay, sumByValues, theStepEvent);
			CtFunnelOverviewRow sumDayAndSumByValuesRow = indexedOverviewRows.get().get(sumDayAndSumByValuesRowKey);
			// 累加到对应的OverviewRow
			sumDayAndSumByValuesRow.setConvertedUser(sumDayAndSumByValuesRow.getConvertedUser() + resultValue);

			if (null != uplayerStepEvent) {
				// 计算本ROW记录转化率
				Double theRate = 0.0D;
				String uplayerSumDayAndSumByValuesRowKey = CtFunnelCalcHelper.buildOverviewRowsKey(sumDay, sumByValues, uplayerStepEvent);
				CtFunnelOverviewRow uplayerRow = indexedOverviewRows.get().get(uplayerSumDayAndSumByValuesRowKey);
				Double uplayerValue = uplayerRow.getConvertedUser();
				theRate = Double.valueOf(0);
				if (uplayerValue > 0) {
					theRate = 100.0D * sumDayAndSumByValuesRow.getConvertedUser() / uplayerValue;
					theRate = CtDoubleUtils.format2Scale(theRate);
				}
				sumDayAndSumByValuesRow.setConversionRate(theRate);
				sumDayAndSumByValuesRow.setCompletionRate(theRate);
			}
		}

		return calcData;
	}

	public CtFunnelCalcData doFunnelDetailStepsRowBuilder(Integer byValuesIndex, CtFunnelCalcData calcData, CtStepEvent theStepEvent, String theDay) {

		String sumDay = calcData.getDateList().get(0);
		List<String> sumByValues = calcData.getByValuesList().get(0);

		log.info("theStepEvent={}", JSON.toJSONString(theStepEvent));
		CtStepEvent uplayerStepEvent = this.getUplayerStepEvent(theStepEvent.getStepNo(), calcData);
		log.info("uplayerStepEvent={}", JSON.toJSONString(uplayerStepEvent));

		// 1.
		List<String> theByValues = calcData.getByValuesList().get(byValuesIndex);
		String calcResultKey = CtFunnelCalcHelper.buildQueryResultKey(theDay, theByValues, theStepEvent);
		Double theResultValue = indexedQueryResults.get().get(calcResultKey);
		if (theResultValue == null) {
			// theResultValue = Double.valueOf(0);
			return calcData;
		}
		// 2.
		String stepsRowKey = CtFunnelCalcHelper.buildStepsRowsKey(theDay, theByValues, theStepEvent);
		CtFunnelDetailStepRow theStepsRow = indexedStepsRows.get().get(stepsRowKey);
		theStepsRow.setConvertedUser(theStepsRow.getConvertedUser() + theResultValue);
		if (null != uplayerStepEvent) {
			// 计算本ROW记录转化率
			Double theRate = 0.0D;
			String uplayerStepsRowKey = CtFunnelCalcHelper.buildStepsRowsKey(theDay, theByValues, uplayerStepEvent);
			CtFunnelDetailStepRow uplayerRow = indexedStepsRows.get().get(uplayerStepsRowKey);
			if (theStepsRow.getConvertedUser() > 0 && uplayerRow.getConvertedUser() > 0) {
				theRate = 100D * theStepsRow.getConvertedUser() / uplayerRow.getConvertedUser();
				theRate = CtDoubleUtils.format2Scale(theRate);
				uplayerRow.setConversionRate(theRate);
			}
		}
		// 3.
		if (!CtFunnelCalcHelper.isSameByValues(sumByValues, theByValues)) {
			// 累加到本Rows中的小计：第一行(条)，即：byValues数组为[$ALL]
			String sumByValuesStepsRowKey = CtFunnelCalcHelper.buildStepsRowsKey(theDay, sumByValues, theStepEvent);
			CtFunnelDetailStepRow sumByValuesStepsRow = indexedStepsRows.get().get(sumByValuesStepsRowKey);
			sumByValuesStepsRow.setConvertedUser(sumByValuesStepsRow.getConvertedUser() + theResultValue);
			// 转化率
			if (null != uplayerStepEvent) {
				// 计算本ROW记录转化率
				Double theRate = 0.0D;
				String uplayerSumByValuesStepsRowKey = CtFunnelCalcHelper.buildStepsRowsKey(theDay, sumByValues, uplayerStepEvent);
				CtFunnelDetailStepRow uplayerRow = indexedStepsRows.get().get(uplayerSumByValuesStepsRowKey);
				if (sumByValuesStepsRow.getConvertedUser() > 0 && uplayerRow.getConvertedUser() > 0) {
					theRate = 100D * sumByValuesStepsRow.getConvertedUser() / uplayerRow.getConvertedUser();
					theRate = CtDoubleUtils.format2Scale(theRate);
					uplayerRow.setConversionRate(theRate);
				}
			}
		}
		// 4.
		String sumDayRowKey = CtFunnelCalcHelper.buildStepsRowsKey(sumDay, theByValues, theStepEvent);
		CtFunnelDetailStepRow sumDayRow = indexedStepsRows.get().get(sumDayRowKey);
		sumDayRow.setConvertedUser(sumDayRow.getConvertedUser() + theResultValue);
		if (null != uplayerStepEvent) {
			// 计算本ROW记录转化率
			Double theRate = 0.0D;
			String uplayerSumDayRowKey = CtFunnelCalcHelper.buildStepsRowsKey(sumDay, theByValues, uplayerStepEvent);
			CtFunnelDetailStepRow uplayerRow = indexedStepsRows.get().get(uplayerSumDayRowKey);
			if (sumDayRow.getConvertedUser() > 0 && uplayerRow.getConvertedUser() > 0) {
				theRate = 100D * sumDayRow.getConvertedUser() / uplayerRow.getConvertedUser();
				theRate = CtDoubleUtils.format2Scale(theRate);
				uplayerRow.setConversionRate(theRate);
			}
		}
		// 5.
		if (!CtFunnelCalcHelper.isSameByValues(sumByValues, theByValues)) {
			String sumDayAndSumByValuesRowKey = CtFunnelCalcHelper.buildStepsRowsKey(sumDay, sumByValues, theStepEvent);
			CtFunnelDetailStepRow sumDayAndSumByValuesRow = indexedStepsRows.get().get(sumDayAndSumByValuesRowKey);
			sumDayAndSumByValuesRow.setConvertedUser(sumDayAndSumByValuesRow.getConvertedUser() + theResultValue);
			if (null != uplayerStepEvent) {
				// 计算本ROW记录转化率
				Double theRate = 0.0D;
				String uplayerRowKey = CtFunnelCalcHelper.buildStepsRowsKey(sumDay, sumByValues, uplayerStepEvent);
				CtFunnelDetailStepRow uplayerRow = indexedStepsRows.get().get(uplayerRowKey);
				if (sumDayRow.getConvertedUser() > 0 && uplayerRow.getConvertedUser() > 0) {
					theRate = 100D * sumDayRow.getConvertedUser() / uplayerRow.getConvertedUser();
					theRate = CtDoubleUtils.format2Scale(theRate);
					uplayerRow.setConversionRate(theRate);
				}
			}
		}

		// 计算funnelDetail 下的一级 completionRate
		// computeRootCompletionRate(calcResult);

		return calcData;
	}

	public CtFunnelCalcData doByValuesBuilder(CtFunnelCalcData calcData, List<LinkedHashMap<String, Object>> queryResults,
			CtCalcFunnelStepReport funnelStep) {

		// 检查并初始化ThreadLocal - indexedByValues
//        if (indexedByValues.get() == null) {
//            LinkedHashMap<String, List<String>> linkedMap = new LinkedHashMap<>();
//            List<String> initByValues = new ArrayList<>();
//            initByValues.add("$ALL");
//            linkedMap.put("$ALL", initByValues);
//            indexedByValues.set(linkedMap);
//        }
		// byValueList & byValues
		StringBuilder indexedByValuesKey = new StringBuilder();
		if (!indexedByValues.get().containsKey("$ALL")) {
			List<String> oneByValues = new ArrayList<>();
			oneByValues.add("$ALL");
			indexedByValues.get().put("$ALL", oneByValues);
			calcData.getByValuesList().add(0, oneByValues);
			calcData.getByValues().add(0, "$ALL");
		}

		// 若没有分组查询，则直接返回
		List<String> byFields = funnelStep.getByFields();
		if (byFields.isEmpty()) {
			return calcData;
		}

		for (LinkedHashMap<String, Object> oneRecord : queryResults) {
			indexedByValuesKey.delete(0, indexedByValuesKey.length());
			List<String> oneByValues = new ArrayList<>();
			for (String oneField : byFields) {
				String shortField = CtCalcPropertyUtils.getProperty(oneField);
				indexedByValuesKey.append(oneRecord.get(shortField)).append("@");
				oneByValues.add(String.valueOf(oneRecord.get(shortField)));
			}
			if (!indexedByValues.get().containsKey(indexedByValuesKey.toString())) {
				indexedByValues.get().put(indexedByValuesKey.toString(), oneByValues);
				calcData.getByValuesList().add(oneByValues);
				//
				if (!calcData.getByValues().contains(oneByValues.get(0))) {
					calcData.getByValues().add(oneByValues.get(0));
				}
			}
		}

		return calcData;
	}

	public CtFunnelCalcData doDateListAndByFieldsBuilder(CtFunnelCalcData calcData, CtCalcFunnelStepReport funnelStep) {
		// 1.dateList
		if (calcData.getDateList().isEmpty()) {
			String fromDate = funnelStep.getFromDate();
			String toDate = funnelStep.getToDate();
			List<String> calcDays = CtCalcDaysUtils.listCalcDayList(fromDate, toDate);
			calcDays.add(0, "$ALL");
			calcData.setDateList(calcDays);
		}
		// 2.byFields
		if (calcData.getByFields().isEmpty()) {
			calcData.getByFields().addAll(funnelStep.getByFields());
		}
		return calcData;
	}

	public CtStepEvent getUplayerStepEvent(Integer thisStepNo, CtFunnelCalcData calcData) {
		if (null == thisStepNo || thisStepNo <= 0) {
			return null;
		}
		List<EventItem> eventItems = calcData.getEvents();
		if (thisStepNo >= eventItems.size()) {
			return null;
		}
		EventItem item = calcData.getEvents().get(thisStepNo - 1);
		CtStepEvent stepEvent = CtStepUtils.buildStepEvent(item.getEventId(), thisStepNo - 1);
		return stepEvent;
	}

}
